package com.dhl.fin.api.service;

import cn.hutool.core.collection.CollectionUtil;
import com.dhl.fin.api.common.dto.OptionDto;
import com.dhl.fin.api.common.dto.QueryDto;
import com.dhl.fin.api.common.enums.ActionEnum;
import com.dhl.fin.api.common.service.CommonService;
import com.dhl.fin.api.common.util.*;
import com.dhl.fin.api.domain.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.joining;

/**
 * Created by CuiJianbo on 2020.02.23.
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class AccountServiceImpl extends CommonService<Account> {
    @Autowired
    RoleServiceImpl roleService;

    @Autowired
    TreeServiceImpl treeService;

    @Autowired
    ProjectServiceImpl projectService;

    @Override
    public void beforeSave(Account domain) throws Exception {
       /* Long[] dataTreeIds = WebUtil.getLongArrayParam("dataTreeIds");
        if (ObjectUtil.notNull(dataTreeIds)) {
            List<Tree> dataPerTrees = Arrays.stream(dataTreeIds).filter(ObjectUtil::notNull).map(id -> new Tree(id)).collect(Collectors.toList());
            domain.setDataPerTrees(dataPerTrees);
        } else {
            List<Tree> trees = new LinkedList<>();
            domain.setDataPerTrees(trees);
        }*/
    }

    @Override
    public void afterGet(Account domain) throws Exception {
        if (CollectorUtil.isNoTEmpty(domain.getRoles())) {
            domain.setRoleId(domain.getRoles().stream().map(Role::getId).map(Object::toString).collect(Collectors.toList()));
        }

        //role list
        Long projectId = WebUtil.getLongParam("projectId");
        QueryDto queryDto = QueryDto.builder().available().addWhere("project.id=" + projectId).build();
        List<OptionDto> optionDtos = roleService.select(queryDto).stream()
                .map(p -> OptionDto.builder().name(p.getName()).value(p.getId().toString()).build())
                .collect(Collectors.toList());
        if (CollectorUtil.isNoTEmpty(optionDtos)) {
            domain.setRoleOptions(optionDtos);
        }


        if (ObjectUtil.notNull(domain.getDataPerTrees())) {
            List<Long> treeIds = domain.getDataPerTrees().stream().map(Tree::getId).collect(Collectors.toList());
            domain.setDataPerTreeIds(treeIds);
        }


        //project list
        List<OptionDto> projectOptions = projectService
                .select(QueryDto.builder().available().build())
                .stream().map(p -> OptionDto.builder().name(p.getName()).value(p.getId().toString()).build())
                .collect(Collectors.toList());

        if (CollectorUtil.isNoTEmpty(projectOptions)) {
            domain.setProjectOptions(projectOptions);
        }

        if (ObjectUtil.notNull(domain.getId())) {
            List<Project> projects = getManageProjects(domain.getId());
            if (CollectorUtil.isNoTEmpty(projects)) {
                List<String> projectIds = projects
                        .stream()
                        .map(Project::getId)
                        .map(Object::toString)
                        .collect(Collectors.toList());
                domain.setManageProjectIds(projectIds);
            }
        }
    }

    @Override
    public String validate(Account domain) throws Exception {
        if (this.actionEnum.equals(ActionEnum.ADD)) {
            Account u = checkUnique(Account.class, "uuid", domain.getUuid());
            if (ObjectUtil.notNull(u) && !u.getRemove()) {
                return "已经存在uuid为" + domain.getUuid() + "的用户";
            }
        }
        return null;
    }

    public Account getUserByUserNo(String userNo) throws Exception {
        QueryDto queryDto = QueryDto.builder()
                .available()
                .addWhere(String.format("account.user_no = '%s'", userNo))
                .build();
        List<Account> accountList = select(queryDto);
        if (CollectorUtil.isNoTEmpty(accountList)) {
            return accountList.get(0);
        }
        return null;
    }

    public List<Role> getRolesByUuid(String uuid, String projectCode) throws Exception {
        QueryDto queryDto = QueryDto.builder()
                .available()
                .addJoinDomain("roles")
                .addWhere(String.format("account.uuid = '%s'", uuid))
                .addWhere(String.format("projects.code= '%s'", projectCode))
                .build();
        List<Account> accountList = select(queryDto);
        if (CollectorUtil.isNoTEmpty(accountList)) {
            return accountList.get(0).getRoles();
        }
        return null;
    }

    public List<Action> getActionsByUuid(String uuid, String projectCode) throws Exception {
        List<Tree> trees = getRoleTree(uuid, projectCode);
        if (CollectorUtil.isNoTEmpty(trees)) {
            List<Long> treeList = trees.stream().map(Tree::getId).collect(Collectors.toList());
            List<Action> actions = trees.stream().map(Tree::getAction).filter(ObjectUtil::notNull).collect(Collectors.toList());
            List<Action> childrenActions = treeService.getAllChildrenByRootIds(treeList, "action")
                    .stream()
                    .map(Tree::getAction)
                    .filter(ObjectUtil::notNull)
                    .collect(Collectors.toList());
            actions.addAll(childrenActions);
            return actions;
        }

        return null;
    }

    /**
     * 通过uui找所有角色下的权限树
     *
     * @param uuid
     * @param projectCode
     * @return
     * @throws Exception
     */
    private List<Tree> getRoleTree(String uuid, String projectCode) throws Exception {
        Account account = get(QueryDto.builder().available().addWhere(String.format("account.uuid = '%s'", uuid)).build());
        if (uuid.equalsIgnoreCase("admin") || account.getIsSuperManager()) {
            Tree menuTree = projectService.get(QueryDto.builder()
                    .available()
                    .addJoinDomain("menuTree")
                    .addWhere(String.format("project.code = '%s'", projectCode)
                    ).build()).getMenuTree();
            return treeService.select(QueryDto.builder().available().addWhere(String.format("tree.parent_id = %s", menuTree.getId())).build());
        } else {
            List<Role> roles = getRolesByUuid(uuid, projectCode);
            if (CollectorUtil.isNoTEmpty(roles)) {
                String roleIds = roles.stream().map(Role::getId).map(Object::toString).collect(joining(","));
                QueryDto queryDto = QueryDto.builder()
                        .available()
                        .addJoinDomain("action")
                        .addWhere(String.format("roles.id in (%s)", roleIds))
                        .build();
                return treeService.select(queryDto);
            }
        }
        return null;
    }

    public List<Menu> getMenusByUuid(String uuid, String projectCode) throws Exception {
        List<Tree> trees = getRoleTree(uuid, projectCode);
        if (CollectorUtil.isNoTEmpty(trees)) {
            String menuTreeCodes = trees.stream()
                    .map(Tree::getParentLine)
                    .filter(ObjectUtil::notNull)
                    .map(p -> p.split("->"))
                    .flatMap(Arrays::stream)
                    .distinct()
                    .collect(joining("','"));

            QueryDto queryDto = QueryDto.builder()
                    .available()
                    .addJoinDomain("menu")
                    .addWhere(String.format("menu.code in ('%s')", menuTreeCodes))
                    .addWhere(String.format("menu.code in ('%s')", menuTreeCodes))
                    .build();
            List<Menu> parentMenus = treeService.select(queryDto)
                    .stream()
                    .map(Tree::getMenu)
                    .filter(ObjectUtil::notNull)
                    .collect(Collectors.toList());

            List<Long> treeList = trees.stream().map(Tree::getId).collect(Collectors.toList());
            List<Menu> childrenMenus = treeService.getAllChildrenByRootIds(treeList, "menu")
                    .stream()
                    .map(Tree::getMenu)
                    .filter(ObjectUtil::notNull)
                    .collect(Collectors.toList());
            parentMenus.addAll(childrenMenus);
            return CollectorUtil.distinct(parentMenus);
        }
        return null;
    }

    public Map<String, List<Tree>> getMenuTreePerProject(String uuid) throws Exception {
        List<Project> projects = getProjectsByUUID(uuid);
        MapBuilder<String, List<Tree>> mapBuilder = MapUtil.builder();
        if (CollectorUtil.isNoTEmpty(projects)) {
            for (Project project : projects) {
                Long menuTreeId = project.getMenuTree().getId();
                List<Tree> menuTrees = treeService.getChildrenByParentId(menuTreeId, "menu", "children");
                if (CollectorUtil.isNoTEmpty(menuTrees)) {
                    for (Tree tree : menuTrees) {
                        if (CollectorUtil.isNoTEmpty(tree.getChildren())) {
                            for (Tree subTree : tree.getChildren()) {
                                subTree.setMenu(treeService.get(subTree.getId(), "menu").getMenu());
                            }

                            if (ObjectUtil.isNull(tree.getChildren().get(0).getMenu())) {
                                tree.setChildren(null);
                            }
                        }
                    }
                }
                mapBuilder.add(project.getCode(), menuTrees);
            }
        }
        return mapBuilder.build();
    }

    public List<Project> getManageProjectsByUUID(String uuid) throws Exception {
        Account account = get(QueryDto.builder().available().addWhere(String.format("account.uuid = '%s'", uuid)).build());
        List<Project> projects = null;
        if (uuid.equalsIgnoreCase("admin") || account.getIsSuperManager()) {
            projects = projectService.select(QueryDto.builder().available().build());
        } else {

            String roleIds = select(QueryDto.builder()
                    .available()
                    .addWhere("roles.code like '%_sys_manager'")
                    .addWhere("account.id = " + account.getId())
                    .build())
                    .stream()
                    .map(Account::getRoles)
                    .flatMap(List::stream)
                    .map(Role::getId)
                    .map(Object::toString)
                    .collect(Collectors.joining(","));

            if (StringUtil.isNotEmpty(roleIds)) {
                projects = projectService.select(QueryDto.builder()
                        .available()
                        .addWhere(String.format("roles.id in  (%s)", roleIds))
                        .build());

            }

        }
        return projects;
    }


    public Account getLoginUser(String uuid) throws Exception {
        Account loginAccount = null;
        if (uuid.equalsIgnoreCase("admin")) {
            loginAccount = new Account();
            loginAccount.setUuid("admin");
            loginAccount.setName("admin");
            loginAccount.setAvailable(true);
            loginAccount.setIsSuperManager(true);
        } else {
            loginAccount = get(QueryDto.builder().available().addWhere(String.format("account.uuid = '%s'", uuid)).build());
        }
        return loginAccount;
    }

    public List<Project> getProjectsByUUID(String uuid) throws Exception {
        Account account = get(QueryDto.builder().available().addWhere(String.format("account.uuid = '%s'", uuid)).build());
        List<Project> projects;
        if (uuid.equalsIgnoreCase("admin") || account.getIsSuperManager()) {
            projects = projectService.select(QueryDto.builder().available().build());
        } else {
            projects = projectService.select(QueryDto.builder().available().addWhere(String.format("accounts.uuid = '%s'", uuid)).build());
        }
        return projects;
    }

    public Map<String, List<Menu>> getMenusPerProject(String uuid) throws Exception {
        List<Project> projects = projectService.select(QueryDto.builder().available().build());
        MapBuilder<String, List<Menu>> mapBuilder = MapUtil.builder();
        if (CollectorUtil.isNoTEmpty(projects)) {
            for (Project project : projects) {
                mapBuilder.add(project.getCode(), getMenusByUuid(uuid, project.getCode()));
            }
        }
        return mapBuilder.build();
    }

    public Map<String, List<Action>> getActionsPerProject(String uuid) throws Exception {
        List<Project> projects = projectService.select(QueryDto.builder().available().build());
        MapBuilder<String, List<Action>> mapBuilder = MapUtil.builder();
        if (CollectorUtil.isNoTEmpty(projects)) {
            for (Project project : projects) {
                mapBuilder.add(project.getCode(), getActionsByUuid(uuid, project.getCode()));
            }
        }
        return mapBuilder.build();
    }

    private List<Project> getManageProjects(Long userId) throws Exception {
        List<Long> roleIds = roleService.select(QueryDto.builder()
                .available()
                .addWhere("role.code like '%_sys_manager'")
                .addWhere("accounts.id = " + userId)
                .build()).stream()
                .map(Role::getId).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(roleIds)) {
            return projectService.select(QueryDto
                    .builder()
                    .available()
                    .addWhere(String.format("roles.id in (%s)", CollectionUtil.join(roleIds, ",")))
                    .build());
        }
        return null;
    }

    @Override
    protected void formatRowData(Account domain) throws Exception {
        List<Project> projects = getManageProjects(domain.getId());
        if (CollectorUtil.isNoTEmpty(projects)) {
            domain.setManageProjectStr(projects.stream().map(p -> p.getName()).collect(joining(",")));
        }

        if (CollectorUtil.isNoTEmpty(domain.getRoles())) {
            String roleStr = domain.getRoles().stream().map(Role::getName).collect(Collectors.joining(","));
            domain.setRoleStr(roleStr);
        }
    }


}
